﻿using AnalyStock.GrapImage;
using AnalyStock.DataManage;

namespace AnalyStock.DataAnalysis;
using static CandlePointsExtension;
/// <summary>
///     填充各类基础数据集合，为批量分析准备
/// </summary>
#region

internal static class DataCollection
{
    public static IList<CandlePoint> Candles;
    //判断K线走势抽样点集合
    public static IList<SamplePoint> Samples;
    //个股主要财务指标集合
    public static IList<DynamicFinancialIndex> Financials;

    public static IList<StockKeyIndicators> SeleStockKeyIndicators;
    //K线日蜡烛集合 
    internal static async ValueTask<bool> IsFilltoBasicDataAsync(string stockCode, CandlePointsParam param)
    {
        return await IsFilltoCandleSeriesAsync(stockCode, param) &&
               await IsFilltoFinancialIndexAsync(stockCode) &&
               await IsFilltoInflectPointsAsync();
    }

    internal static async Task<IEnumerable<StockInfo>> FilltoStockInfosAsync()
    {
        try
        {
            return await CommDataHelper<StockInfo>.GetDataOnDbSetAsync()
                                                   .ConfigureAwait(false);
            //return stockInfos?.Where(n => !n.Name.Contains("ST")); //不含ST股,只对列表的引用
        }
        catch (Exception ex)
        {
            ErrorLogPool.LogErrorMessage("StockInfos", "all", "", ex.Message);
            return null;
        }
    }
    private static async ValueTask<bool> IsFilltoCandleSeriesAsync(string stockCode, CandlePointsParam param)
    {
        try
        {
            var _dailes = await CommDataHelper<Daily>.GetDataOnDbSetAsync(stockCode,
                                                                          DateTime.Today.AddMonths(-param.SelectMonthsCount).ToDateString(),
                                                                          DateTime.Today.ToDateString())
                                                     .ConfigureAwait(false);
            //没有记录，抛出异常放弃执行
            if (_dailes.IsNullOrEmpty())
            {
                throw new NullReferenceException("没有交易数据...");
            }

            if (param.IsFillCurrentDailyOnline)
            {
                _dailes = await _dailes.AddCurrentDailyOnlineAsync().ConfigureAwait(false);
            }
            //此处只是用接口，不进行实例化，为提高批量取数的效率
            Candles.Dispose();
            Candles = _dailes.CreatCandlePoints(param.PeriodType);
            return !Candles.IsNullOrEmpty();
        }
        catch (Exception ex)
        {
            ErrorLogPool.LogErrorMessage("Daily", stockCode, "", ex.Message);
            return false;
        }
    }

    private static async ValueTask<bool> IsFilltoInflectPointsAsync()
    {
        await Task.Delay(2);
        Samples.Dispose();
        Samples = Candles?.CreateInflectPointsByClose(0.05f);
        return !Samples.IsNullOrEmpty();
    }

    private static async ValueTask<bool> IsFilltoFinancialIndexAsync(string stockCode)
    {
        try
        {
            Financials.Dispose();
            Financials = await CommDataHelper<DynamicFinancialIndex>.GetDataOnDbSetAsync(stockCode,
                                                                      DateTime.Today.AddMonths(-12).ToDateString(),
                                                                      DateTime.Today.ToDateString())
                                                                    .ConfigureAwait(false);
            return !Financials.IsNullOrEmpty();
        }
        catch (Exception ex)
        {
            ErrorLogPool.LogErrorMessage("Financialindex", CandlePoints[0].StockCode, "", ex.Message);
            return false;
        }
    }
}
#endregion

/// <summary>
/// 查询K线蜡烛点扩展方法
/// </summary>
internal static class CandlePointsExtension
{
    //倍量,当日为红色倍量,当日绿色倍量，涨停
    internal enum CandleType
    {
        DoubleVol, RedDoubleVol, GreenDoubleVol, SkyrocKeting, All
    }
    private static Func<CandlePoint, bool> SelectPredicate(CandleType qtype)
    {
        return qtype switch
        {
            CandleType.DoubleVol => p => p.IsDoubleVol,
            CandleType.RedDoubleVol => p => p.IsDoubleVol && p.IsCloseUpOpen,
            CandleType.SkyrocKeting => p => p.IsSkyrocketing,
            CandleType.All => n => true,
            _ => n => false
        };
    }

    internal static IEnumerable<CandlePoint> TakePoints(
        this IList<CandlePoint> list, CandleType qtype)
    {
        return list.Where(SelectPredicate(qtype));
    }

    internal static IEnumerable<CandlePoint> TakeLastDayPoints(
        this IList<CandlePoint> list, CandleType seleQueryType, int days)
    {
        return list.TakeLast(days).Where(SelectPredicate(seleQueryType));
    }

    internal static IEnumerable<float> TakeCloseArray(
        this IList<CandlePoint> list)
    {
        return list.Select(n => n.Close).ToArray();
    }

    internal static IEnumerable<float> TakeCloseArray(
        this IList<CandlePoint> list, Func<CandlePoint, bool> wherefunc)
    {
        return list.Where(wherefunc).Select(n => n.Close);
    }

}
/// <summary>
/// 查询K线拐点集合扩展方法
/// </summary>
internal static class InflectPointsExtension
{
    internal enum InflectPType
    {
        //顶部点，底部点，起始点，结尾点，全部点
        Top, Bottom, NotTop, NotBottom, NotStartEnd, Start, End, All
    }

    //Func的模式，会将全表提取到内存中执行，大型查询不建议使用
    private static Func<SamplePoint, bool> SelectPredicate(InflectPType qtype)
    {
        return qtype switch
        {
            InflectPType.Top => p => p.TrendType is "↓", //顶点
            InflectPType.NotBottom => p => p.TrendType is not "↑", //包括顶点、StartP,EndP            
            InflectPType.Bottom => p => p.TrendType is "↑", //底点
            InflectPType.NotTop => p => p.TrendType is not "↓", //包括底点，startP，EndP
            InflectPType.NotStartEnd => p => p.TrendType is not ("start" or "end"), //不包括初始和结尾点
            InflectPType.Start => p => p.TrendType is "start", //初始点
            InflectPType.End => p => p.TrendType is "end", //结尾点
            InflectPType.All => p => true,
            _ => p => false
        };
    }

    internal static IEnumerable<SamplePoint> TakePoints(
        this IList<SamplePoint> list, InflectPType queryType)
    {
        return list.Where(SelectPredicate(queryType));
    }

    internal static IEnumerable<float> TakeCloseArray(
        this IList<SamplePoint> list, InflectPType qtype)
    {
        return list.Where(SelectPredicate(qtype)).Select(n => n.Close);
    }
    internal static IEnumerable<T1> TakeArray<T1>(this IEnumerable<SamplePoint> ienum, Func<SamplePoint, T1> result)
    {
        return ienum.Select(result);
    }
    internal static IEnumerable<T1> TakeArray<T, T1>(this IEnumerable<T> ienumT, T1 t)
    {
        static Func<T, T1> func(T1 t)
        {
            return T => t;
        }
        return ienumT.Select(func(t));
    }
}


/// <summary>
///     生成走势中各类点的点点值，K线拐点、模拟曲线的点值、倍量的点值、财务EPS点值
/// </summary>
internal static class CreatPaintPoints
{
    // static readonly float rateWave = 0.05f;
    /// <summary>
    ///     获取价格的拐点数据，形成拐点系列值，依据价格变动的幅度：波动率
    /// </summary>
    /// <param name="candleSeries"></param>
    /// <param name="rateWave"></param>
    /// <returns></returns>
    public static IList<SamplePoint> CreateInflectPointsByClose(this IList<CandlePoint> list, float rateWave)
    {
        List<SamplePoint> _inflects = [];
        if (list.Count < 2)
        {
            return _inflects;
        }
        foreach (var item in
                 list.TakeCloseArray().CalcIndexOnInflectPoint(rateWave))
        {
            _inflects.Add(new SamplePoint
            {
                LocationIndex = item.Key,
                StockCode = list[item.Key].StockCode,
                TradeDate = list[item.Key].Date,
                Close = list[item.Key].Close,
                FittingValue = list[item.Key].Close,
                TrendType = item.Value,
                PeriodType = "daily"
            });
        }
        return _inflects;
    }

    /// <summary>
    ///     获取拐点线的趋势线，模拟趋势方程为3次函数
    /// </summary>
    /// <param name="candleSeries"></param>
    /// <param name="rateWave"></param>
    /// <returns></returns>
    public static (IList<SamplePoint> FittingPoints, double[] FittingParam)
        CreateNorderFittingPointsBySample(this IList<CandlePoint> list, int n_order)
    {
        var _samples = list.FillSamplePointsByClose();
        var _sampleNorders = new List<SamplePoint>();
        if (_samples.Count < n_order) return (_sampleNorders, null);
        //线性模型           
        var _fittingParams = MathNet.Numerics.Fit.Polynomial(
                _samples.Select(n => (double)n.LocationIndex).ToArray(),
                _samples.Select(n => (double)n.Close).ToArray(), n_order);

        foreach (var item in _samples)
        {
            var (FuncExpression, FittingValue, Derivative)
                = CommCalculate.CalcFunctionValue(_fittingParams, item.LocationIndex);
            _sampleNorders.Add(new SamplePoint
            {
                LocationIndex = item.LocationIndex,
                TradeDate = item.TradeDate,
                StockCode = item.StockCode,
                Close = item.Close,
                FittingValue = FittingValue,
                Derivative = Derivative,
                FuncExpression = FuncExpression,
                TrendType = "FittingFunction",
                PeriodType = "daily"
            });
        }

        _samples.Dispose();
        return (_sampleNorders, _fittingParams);
    }

    /// <summary>
    ///     获取交易量倍量点的所有点，当日交易最低价
    /// </summary>
    /// <param name="candleSeries"></param>
    /// <returns></returns>
    public static IList<SamplePoint> CreateDoubleVolPointsByLow(this IList<CandlePoint> list)
    {
        List<SamplePoint> attacks = [];
        if (list.Count < 2)
        {
            return attacks;
        }

        foreach (var item in list.TakePoints(CandleType.DoubleVol))
        {
            attacks.Add(new SamplePoint
            {
                LocationIndex = item.LocationIndex,
                StockCode = item.StockCode,
                TradeDate = item.Date,
                TrendType = "DoubleVol",
                Close = item.Close,
                FittingValue = item.Low,
                PeriodType = "daily"
            });
        }

        return attacks;
    }

    /// <summary>
    ///     获取EPS的图像数据，及坐标索引
    /// </summary>
    /// <param name="candleSeries"></param>
    /// <returns></returns>
    public static IList<EPSPoint> CreatFinacialEPSPoints(this IList<DynamicFinancialIndex> list)
    {
        List<EPSPoint> epsPoints = [];
        if (list.IsNullOrEmpty())
        {
            return epsPoints;
        }

        foreach (var (ReportDt, EPS) in list.Select(n => (n.End_date, n.Eps)))
        {
            var candle = CandlePoints.Last(n => n.Month == ReportDt[..6]);
            if (candle != null)
            {
                epsPoints.Add(new EPSPoint
                {
                    LocationIndex = candle.LocationIndex,
                    ReportDate = ReportDt,
                    EPS = EPS
                });
            }
        }

        return epsPoints;
    }

    public static IList<NetAssetPSPoint> CreatNetAssetPSPoints(this IList<DynamicFinancialIndex> list)
    {
        List<NetAssetPSPoint> napsPoints = [];
        if (list.IsNullOrEmpty())
        {
            return napsPoints;
        }

        foreach (var (ReportDt, NetAssetPS) in list.Select(n => (n.End_date, n.Bps)))
        {
            var candle = CandlePoints.Last(n => n.Month == ReportDt[..6]);
            if (candle != null)
            {
                napsPoints.Add(new NetAssetPSPoint
                {
                    LocationIndex = candle.LocationIndex,
                    ReportDate = ReportDt,
                    NetAssetPS = NetAssetPS
                });
            }
        }

        return napsPoints;
    }

    private static List<SamplePoint> FillSamplePointsByClose(this IList<CandlePoint> list)
    {
        List<SamplePoint> _samples = [];
        if (list.Count < 5)
        {
            return _samples;
        }

        var _maxCloseCandle = list.MaxBy(n => n.Close);
        var _minCloseCandle = list.MinBy(n => n.Close);
        var _idxDivSection = list.Count / 27.0f; //3个部分，每部分9个取值点
        var _indexDiv = 0;
        for (var i = 0; i <= 27; i++)
        {
            _indexDiv = i * _idxDivSection < 2 ? 0 : (int)(i * _idxDivSection) - 1;
            _samples.Add(new SamplePoint
            {
                LocationIndex = _indexDiv,
                StockCode = list[_indexDiv].StockCode,
                TradeDate = list[_indexDiv].Date,
                Close = list[_indexDiv].Close,
                PeriodType = "Daily"
            });
        }

        _samples.Add(new SamplePoint
        {
            LocationIndex = _maxCloseCandle.LocationIndex,
            StockCode = _maxCloseCandle.StockCode,
            TradeDate = _maxCloseCandle.Date,
            Close = _maxCloseCandle.Close,
            PeriodType = "Daily"
        });
        _samples.Add(new SamplePoint
        {
            LocationIndex = _minCloseCandle.LocationIndex,
            StockCode = _minCloseCandle.StockCode,
            TradeDate = _minCloseCandle.Date,
            Close = _minCloseCandle.Close,
            PeriodType = "Daily"
        });
        return _samples.DistinctBy(n => n.LocationIndex)
                       .OrderBy(n => n.LocationIndex)
                       .ToList();
    }
}



///分析中使用个数据点位值或截面值，结构类    

#region

/// <summary>
///     财务指标EPS的点位值
/// </summary>
public class EPSPoint
{
    public int LocationIndex { get; set; }
    public string ReportDate { get; set; }
    public float EPS { get; set; }
}

public class NetAssetPSPoint
{
    public int LocationIndex { get; set; }
    public string ReportDate { get; set; }
    public float NetAssetPS { get; set; }
}

/// <summary>
///     K线走势中拐点的点位值
/// </summary>
public class SamplePoint
{
    public int LocationIndex { set; get; } //坐标轴上的索引位置，不是具体索引
    public string StockCode { set; get; }
    public string TradeDate { get; set; }
    public float Close { get; set; } //收盘价
    public string PeriodType { get; set; } //期间类型
    public string TrendType { get; set; } // "↓" 上部拐点，走势方向向下了；"↑"：下部拐点，走势方向向上
    public float FittingValue { get; set; } //选取点的收盘价或模型值       
    public float Derivative { get; set; } //导数值，斜率； derivative     
    public string FuncExpression { get; set; } //函数表达式       
}

/// <summary>
///     K线走势中顶部拐点的点位值
/// </summary>
public class TopInflectPoint
{
    public int LocationIndex { get; set; }
    public float PointValue { get; set; } //该点的价格
    public float GrowToCurrent { get; set; } //该点到当前的价格增长幅度
    public bool IsSkyRocketing { get; set; } //是否涨停
    public int DaysToCurrent { get; set; } //该高点到当前的日期数
}

/// <summary>
///     取得交易区间主要的趋势各类指标
/// </summary>
public class TrendIndexInPeriod
{
    public string StockCode { set; get; } //股票码
    public string StockName { set; get; }
    public float Current { get; set; } //当前收盘价
    public float Max { get; set; } //最高收盘价
    public float Min { get; set; } //最低收盘价        
    public float WaveRate { get; set; } //收盘价振荡幅度
    public float RatioCurrentToMax { get; set; } //收盘价距最高价比例，位置
    public int MaxIdx { get; set; } //最高价的索引
    public int MinIdx { get; set; } //最低价的索引                      
    public int DaysToMax { get; set; } //当前距离最高价的交易日数
    public int DaysToMin { get; set; } //当前距离最低价的交易日数
    public int DoubleVolCountsIn20Days { get; set; } //20日内的交易量倍量数
}

/// <summary>
///     最近的财务指标值
/// </summary>
public class LastFinaIndex
{
    public float Eps { get; set; } //最近的EPS
    public float FloatMv { get; set; } //最近的流通市值
    public float TotalMv { get; set; }
}

#endregion